//
// Application.cpp
//
// $Id: //poco/1.4/Util/src/Application.cpp#6 $
//
// Library: Util
// Package: Application
// Module:  Application
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#include "Poco/Util/Application.h"
#include "Poco/Environment.h"
#include "Poco/Exception.h"
#include "Poco/File.h"
#include "Poco/Path.h"
#include "Poco/String.h"
#include "Poco/ConsoleChannel.h"
#include "Poco/Logger.h"
#include "Poco/Util/OptionProcessor.h"
#include "Poco/Util/PropertyFileConfiguration.h"
#include "Poco/Util/IniFileConfiguration.h"
#if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS)
#include "Poco/SignalHandler.h"
#endif


using Poco::Logger;
using Poco::Path;
using Poco::File;
using Poco::Environment;
using Poco::SystemException;
using Poco::ConsoleChannel;
using Poco::icompare;


namespace Poco {
namespace Util {


Application* Application::m_pInstance = 0;


Application::Application():
	m_pConfig(new LayeredConfiguration()),
	m_initialized(false),
	m_unixOptions(true),
	m_pChannel(new SplitterChannel()),
	m_stopOptionsProcessing(false)
{
	setup();
}


Application::Application(int argc, char* argv[]):
	m_pConfig(new LayeredConfiguration()),
	m_initialized(false),
	m_unixOptions(true),
	m_pChannel(new SplitterChannel()),
	m_stopOptionsProcessing(false)
{
	setup();
	init(argc, argv);
}


Application::~Application()
{
	delete m_pChannel;
	m_pInstance = 0;
}


void Application::setup()
{
	poco_assert (m_pInstance == 0);
	m_pLogger = &Logger::get("");		/// 默认logger
	m_pConfig->add(new PropertyFileConfiguration, CONFIG_APPLICATION, true);
#if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS)
	m_workingDirAtLaunch = Path::current();
	Poco::SignalHandler::install(SIGILL,SIG_DFL);
	Poco::SignalHandler::install(SIGBUS,SIG_DFL);
	Poco::SignalHandler::install(SIGSEGV,SIG_DFL);
	Poco::SignalHandler::install(SIGSYS,SIG_DFL);
#else
	setUnixOptions(false);
#endif

	m_pInstance = this;

	ConsoleChannel* pCC = new ConsoleChannel;
	m_pChannel->addChannel(Channel_Type_Console,pCC);
	Logger::setChannel("", m_pChannel);
}


void Application::init(int argc, char* argv[])
{
	setArgs(argc, argv);
	init();
}


void Application::init(const std::vector<std::string>& args)
{
	setArgs(args);
	init();
}


void Application::init()
{
	Path appPath;
	getApplicationPath(appPath);
	m_pConfig->setString("application.path", appPath.toString());
	m_pConfig->setString("application.name", appPath.getFileName());
	m_pConfig->setString("application.baseName", appPath.getBaseName());
	m_pConfig->setString("application.dir", appPath.parent().toString());
	m_pConfig->setString("application.configDir", appPath.parent().toString());
	processOptions();
}


const char* Application::name() const
{
	return "Application";
}


void Application::initialize(Application& self)
{
	m_initialized = true;
}

	
void Application::uninitialize()
{
	if (m_initialized)
	{
		m_initialized = false;
	}
}

void Application::reinitialize(Application& self)
{
	uninitialize();
	initialize(self);
}

void Application::setUnixOptions(bool flag)
{
	m_unixOptions = flag;
}


int Application::loadConfiguration(int priority)
{
	int n = 0;
	Path appPath;
	getApplicationPath(appPath);
	Path	confPath;
	if (findAppConfigFile(appPath.getBaseName(), "properties", confPath))
	{
		m_pConfig.get()->add(new PropertyFileConfiguration(confPath.toString()), priority, false);
		++n;
	}

	if (findAppConfigFile(appPath.getBaseName(), "ini", confPath))
	{
		m_pConfig.get()->add(new IniFileConfiguration(confPath.toString()), priority, false);
		++n;
	}
	if (n > 0)
	{
		if (!confPath.isAbsolute())
			m_pConfig.get()->setString("application.configDir", confPath.absolute().parent().toString());
		else
			m_pConfig.get()->setString("application.configDir", confPath.parent().toString());
	}

	return n;
}

void Application::loadConfiguration(const std::string& path, int priority)
{
	int n = 0;
	Path confPath(path);
	std::string ext = confPath.getExtension();
	if (icompare(ext, "properties") == 0)
	{
		m_pConfig.get()->add(new PropertyFileConfiguration(confPath.toString()), priority, false);
		++n;
	}
	else if (icompare(ext, "ini") == 0)
	{
		m_pConfig.get()->add(new IniFileConfiguration(confPath.toString()), priority, false);
		++n;
	}
	else throw Poco::InvalidArgumentException("Unsupported configuration file type", ext);

	if (n > 0 && !m_pConfig.get()->has("application.configDir"))
	{
		if (!confPath.isAbsolute())
			m_pConfig.get()->setString("application.configDir", confPath.absolute().parent().toString());
		else
			m_pConfig.get()->setString("application.configDir", confPath.parent().toString());
	}
}


void Application::stopOptionsProcessing()
{
	m_stopOptionsProcessing = true;
}


int Application::run()
{
	int rc = EXIT_CONFIG;
	initialize(*this);

	try
	{
		rc = EXIT_SOFTWARE;
		rc = main(m_unprocessedArgs);
	}
	catch (Poco::Exception& exc)
	{
		logger().log(exc);
	}
	catch (std::exception& exc)
	{
		logger().error(exc.what());
	}
	catch (...)
	{
		logger().fatal("system exception");
	}

	uninitialize();
	return rc;
}


int Application::main(const std::vector<std::string>& args)
{
	return EXIT_OK;
}


void Application::setArgs(int argc, char* argv[])
{
	m_command = argv[0];
	m_unprocessedArgs.reserve(argc);
	for (int i = 0; i < argc; ++i)
	{
		std::string arg(argv[i]);
		m_unprocessedArgs.push_back(arg);
	}
}


void Application::setArgs(const std::vector<std::string>& args)
{
	poco_assert (!args.empty());
	
	m_command = args[0];
	m_unprocessedArgs = args;
}


void Application::processOptions()
{
	defineOptions(m_options);
	OptionProcessor processor(m_options);
	processor.setUnixStyle(m_unixOptions);
	m_argv = m_unprocessedArgs;
	m_unprocessedArgs.erase(m_unprocessedArgs.begin());
	std::vector<std::string>::iterator it = m_unprocessedArgs.begin();
	while (it != m_unprocessedArgs.end() && !m_stopOptionsProcessing)
	{
		std::string name;
		std::string value;
		if (processor.process(*it, name, value))
		{
			if (!name.empty()) // "--" option to end options processing or deferred argument
			{
				handleOption(name, value);
			}
			it = m_unprocessedArgs.erase(it);
		}
		else ++it;
	}
	if (!m_stopOptionsProcessing)
		processor.checkRequired();
}


void Application::getApplicationPath(Poco::Path& appPath) const
{
#if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS)
	if (m_command.find('/') != std::string::npos)
	{
		Path path(m_command);
		if (path.isAbsolute())
		{
			appPath = path;
		}
		else
		{
			appPath = m_workingDirAtLaunch;
			appPath.append(path);
		}
	}
	else
	{
		if (!Path::find(Environment::get("PATH"), m_command, appPath))
			appPath = Path(m_workingDirAtLaunch, m_command);
		appPath.makeAbsolute();
	}
#else
	appPath = m_command;
#endif
}


bool Application::findFile(Poco::Path& path) const
{
	if (path.isAbsolute()) return true;
	
	Path appPath;
	getApplicationPath(appPath);
	Path base = appPath.parent();

	Path p(base, path);
	p.makeAbsolute(base);
	File f(p);
	if (f.exists())
	{
		path = p;
		return true;
	}
	return false;
}


bool Application::findAppConfigFile(const std::string& appName, const std::string& extension, Path& path) const
{
	poco_assert (!appName.empty());

	Path p(appName);
	p.setExtension(extension);
	bool found = findFile(p);
	if (found)
		path = p;
	return found;
}


void Application::defineOptions(OptionSet& options)
{
	
}


void Application::handleOption(const std::string& name, const std::string& value)
{
	const Option& option = m_options.getOption(name);
	if (!option.binding().empty())
	{
		AbstractConfiguration* pConfig = option.config();
		if (!pConfig) pConfig = &config();
		pConfig->setString(option.binding(), value);
	}
	if (option.callback())
	{
		option.callback()->invoke(name, value);
	}
}


void Application::addChannel(Channel_Type type, Channel* pChannel)
{
	if (pChannel)
	{
		m_pChannel->removeChannel(type);
		m_pChannel->addChannel(type,pChannel);
	}
}



} } // namespace Poco::Util
